package cn.tsa.share.jdbc.shardingjdbc;

import cn.hutool.core.collection.CollUtil;
import cn.tsa.share.jdbc.enums.ShardingColumnEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 用于多分片键的复合分片场景
 *
 * @author hongwei
 */
@Slf4j
public class OrderComplexShardingAlgorithm implements ComplexKeysShardingAlgorithm<String> {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, ComplexKeysShardingValue<String> shardingValue) {
        List<String> shardingResults = new ArrayList<>();
        String logicTableName = shardingValue.getLogicTableName();
        //获取 列名-值-方式1，范围查询
//        Map<String, Range<String>> columnNameAndRangeValuesMap = shardingValue.getColumnNameAndRangeValuesMap();
        //获取 列名-值方式2，in查询
        Map<String, Collection<String>> columnNameAndShardingValuesMap = shardingValue.getColumnNameAndShardingValuesMap();
        Collection<String> orderTypeValue = columnNameAndShardingValuesMap.get(ShardingColumnEnum.ORDER_TYPE.getValue());
        Collection<String> statusTypeValue = columnNameAndShardingValuesMap.get(ShardingColumnEnum.STATUS_TYPE.getValue());
        Map<Integer, String> tables = availableTargetNames.stream().collect(Collectors.toMap(tableName -> {
            String[] split = tableName.split(logicTableName);
            return Integer.valueOf(split[split.length - 1]);
        }, Function.identity()));
        Set<Integer> indexArray = tables.keySet();
        Integer orderTypeIndex = -1;
        Integer statusTypeIndex = -2;
        if (orderTypeValue != null && statusTypeValue != null) {
            for (String orderType : orderTypeValue) {
                int value = Integer.valueOf(orderType);
                if (indexArray.contains(value)) {
                    orderTypeIndex = value;
                }
            }
            for (String statusType : statusTypeValue) {
                int value = Integer.valueOf(statusType);
                if (indexArray.contains(value)) {
                    statusTypeIndex = value;
                }
            }
        }
        if ((orderTypeIndex > 0 && statusTypeIndex > 0) && orderTypeIndex.equals(statusTypeIndex)) {
            shardingResults.add(tables.get(statusTypeIndex));
        } else {
            shardingResults.add(logicTableName);
        }

        return shardingResults;
    }


    private boolean validateAndPopulateShardingValueList(List<String> shardingResults, Collection<String> tableNames,
                                                         Map<String, Collection<String>> columnNameAndShardingValuesMap,
                                                         String logicTableName) {
        return Arrays.stream(ShardingColumnEnum.values())
                .map(ShardingColumnEnum::getValue)
                .allMatch(v -> {
                    //insert value
                    Collection<String> shardingValues = columnNameAndShardingValuesMap.get(v);
                    if (CollUtil.isEmpty(shardingResults) && shardingValues.contains("")) {
                        return false;
                    }
                    return shardingValues.stream()
                            .allMatch(shardingValue -> tableNames.stream()
                                    .allMatch(tableName -> {
                                        String tableNameWithShardingValue = tableNameWithShardingValue(tableName, shardingValue, logicTableName);
                                        if (Objects.nonNull(tableNameWithShardingValue)) {
                                            shardingResults.add(tableNameWithShardingValue);
                                            return true;
                                        }
                                        return false;
                                    })
                            );
                });
    }

    private String tableNameWithShardingValue(String tableName, String shardingValue, String logicTableName) {
        return Optional.ofNullable(tableName)
                .map(String::toLowerCase)
                .map(t -> t.split(logicTableName))
                .map(split -> split[split.length - 1])
                .map(indexEnd -> {
                    int value = Integer.valueOf(shardingValue);
                    int index = Integer.valueOf(indexEnd);
                    if (value == index) {
                        return tableName;
                    } else {
                        return logicTableName;
                    }
                }).orElse(null);
    }
}
